<?php
/**
* File 클래스 정의
*
* method collection about file/directory
*
* @package Utility
* @author Yangkun <yangkun7@kornet.net>
* @version 5.3.0
*/
if (!class_exists('File'))
{
	class File
	{
		/**
		* PHP 5 Constructor
		**/
		function __construct()
		{

		}

		/**
		* Constructor
		*/
		function File()
		{
			$this->__construct();
		}

		/**
		* 파일 업로드 하기전에 파일목록이 모두 업로드 가능한지 사전 체크
		*
		* @access public
		* @since v1
		* @param array $files 업로드된 파일정보를 담은 배열 ($_FILES)
		* @param string $dir 저장할 디렉토리
		* @param string $method_on_dup 중복 파일 존재시 처리 방법
		* @param array $exts 확장자 목록
		* @param bool $allow_list $ext 가 업로드 가능 목록인지...
		* @param string $to_name 파일명 변경시 사용
		* @return string 에러 메시지.. empty 이면 에러 없음...
		**/
		function can_upload(&$files, $save_directory, $method_on_dup = "rename",  $exts = array(), $allow_list = TRUE, $to_name = "", $postfix = "")
		{
			$msg = "";	// 에러 메시지 끝까지 메시지가 비어 있다면... 성공!
			// 디렉토리가 존재하는지 검사
			if (!is_dir($save_directory)) $msg = "{$save_directory} 은 디렉토리가 아닙니다.";
			// 디렉토리에 쓸 수 있는지 검사
			if (empty($msg) && !is_writable($save_directory)) $msg = "{$save_directory} 에 쓸 수 있는 권한이 없습니다.";
			if (!empty($msg)) return $msg;
			// 각 업로드 파일 검사
			foreach ($files as $file)
			{
				if (empty($file['name'])) continue;
				// file error check
				switch ($file['error'])
				{
					case UPLOAD_ERR_INI_SIZE:
						$msg = "[{$file['name']}]\\n파일의 용량이 설정된 용량을 초과힙니다.\\n관리자에게 문의하세요.";
						break;
					case UPLOAD_ERR_FORM_SIZE:
						$msg = "[{$file['name']}]\\n업로드한 파일이 지정한 사이즈 보다 큽니다.";
						break;
					case UPLOAD_ERR_PARTIAL:
						$msg = "[{$file['name']}]\\n파일이 일부분만 전송되었습니다. ";
						break;
				}
				if (!empty($msg)) break;
				//// 확장자 검사...
				$ext = strrchr($file['name'], ".");
				$ext = strtolower($ext);
				// 확장자
				$file_name = substr($file['name'], 0, strlen($file['name']) - strlen($ext));	// 확장자를 뺀 파일이름
				if (in_array($ext, $exts) != $allow_list)
				{
					$str = ($allow_list) ? "업로드 허가" : "업로드 불가";
					$ext_list = implode(" / ", $exts);
					$msg = "[{$file['name']}]\\n{$ext} 확장자를 가진 파일은 업로드 불가합니다.\\n\\n{$str} 확장자 : {$ext_list}";
					break;
				}
				///// 파일 중복 검사..
				if ($method_on_dup == "error")
				{
					if (!empty($to_name)) $file_name = $to_name;
					$slash = ($save_directory[strlen($save_directory) - 1] != "/") ? "/" : "";
					$target = $save_directory.$slash.$file_name.$postfix.$ext;
					if (file_exists($target))
					{
						$msg = "[{$file['name']}]\\n동일한 이름의 파일이 존재합니다.";
						break;
					}
				}// 파일 중복검사
			}// 각 업로드 파일 검사
			// 에러 메시지 리턴. 비어 있으면 에러 없음.
			return $msg;
		}

		/**
		* @see can_upload()
		* @version 5.2.0
		**/
		function can_a_upload($file, $save_directory, $method_on_dup = "rename",  $exts = array(), $allow_list = TRUE, $to_name = "", $postfix = "")
		{
			$files[] = $file;
			return $this->can_upload($files, $save_directory, $method_on_dup, $exts, $allow_list, $to_name, $postfix);
		}

		/**
		* 특정 파일을 삭제한다.
		* @param string $filename 삭제할 파일의 경로
		* @param boolean $is_check 파일존재여부 체크
		* @return boolean 성공하면 TRUE, 실패하면 FALSE.
		**/
		function delete_a_file($filename, $is_check=false)
		{
			if ($is_check && !file_exists($filename)) return true;
			return unlink($filename);
		}

		/**
		* 주어진 $dir 디렉토리를 리커시브하게 삭제한다.
		*
		* @access public
		* @since v1
		* @param string $dir 삭제할 디렉토리 (절대경로)
		* @return bool 성공 TRUE
		**/
		function delete_directory($dir)
		{
			if (!is_dir($dir)) user_error("{$dir} is not a Directory", E_USER_ERROR);
			$handle = opendir($dir) or user_error("Can not open the directory ({$dir})", E_USER_ERROR);
			while (false!==($FolderOrFile = readdir($handle)))
			{
				if($FolderOrFile != "." && $FolderOrFile != "..")
				{
					if(is_dir("$dir/$FolderOrFile"))
					{ $this->delete_directory("$dir/$FolderOrFile"); }  // recursive
					else
					{ @unlink("$dir/$FolderOrFile"); }
				}
			}
			@closedir($handle);
			$success = false;
			if(rmdir($dir))
			{ $success = true; }
			return $success;
		}

		/**
		* $dir 에 존재하는 디렉토리 목록을 리턴
		*
		* @since v2
		* @param string $dir 검색할 디렉토리
		* @param string $pattern 검사할 디렉토리 패턴 (정규표현식)
		* @return array
		**/
		function & get_dirs($dir, $pattern = '')
		{
			$list = array();
			$handle = opendir($dir) or user_error("CANNOT_OPEN_DIRECOTRY : {$dir}", E_USER_ERROR);
			while ($filename = readdir($handle))
			{
				if ($filename != '.' && $filename != '..' && is_dir($dir . '/' . $filename))
				{
					if (empty($pattern) || (!empty($pattern) && ereg($pattern, $filename))) $list[] = $filename;
				}
			}
			if ($handle) closedir($handle);
			return $list;
		}

		/**
		* $start_dir 에 존재하는 디렉토리 목록을 리턴
		*
		* @param string $start_dir 시작 경로
		* @param array $arr 결과를 저장할 배열 참조
		* @param bool $prefix_start 시작경로를 파일 경로앞에 추가할지 여부
		* @param bool $recursive 하위 디렉토리 포함 여부
		* @param bool $prefix_dir 디렉토리명에 첨부될 디렉토리경로
		* @param string $pattern 검사할 디렉토리 패턴 (정규표현식)
		* @return array
		* @version 5.2.0
		* @author hamhaja <hamhaja@nextez.co.kr>
		**/
		function & get_dirnames($start_dir, &$arr, $prefix_start = true, $recursive = true, $prefix_dir = '', $pattern = '')
		{
			$handle = opendir($start_dir) or user_error("CANNOT_OPEN_DIRECOTRY : {$start_dir}", E_USER_ERROR);
			while ($filename = readdir($handle))
			{
				if ($filename != '.' && $filename != '..' && is_dir($start_dir . '/' . $filename))
				{
					if (empty($pattern) || (!empty($pattern) && ereg($pattern, $filename)))
					{
						$path = $start_dir . "/" . $filename;
						if ($prefix_start) $arr[] = $path;
						else $arr[] = $prefix_dir . $filename;

						if ($recursive)
						{
							$prefix_path = $prefix_dir . $filename . '/';
							$this->get_dirnames($path, $arr, $prefix_start, $recursive, $prefix_path, $pattern);
						}
					}
				}
			}

			if ($handle) closedir($handle);
		}

		/**
		* 파일의 확장자를 얻는다.
		* @param string $filename 파일의 경로
		* @return string "." 을 포함한 파일의 확장자
		**/
		function get_extension($filename)
		{
			$filename = trim($filename);
			$ext = strrchr($filename, ".");
			return strtolower($ext);
		}

		/**
		* 확장자를 뺀 파일경로를 반환..
		* @param string $filename 파일경로
		* @return string 확장자를 제외한 파일경로
		*/
		function get_filename($filename)
		{
			$ext = strrchr($filename, ".");
			$name = substr($filename, 0, strlen($filename) - strlen($ext));

			return $name;
		}

		/**
		* 특정 디렉토리에서 지정한 패턴에 해당하는 파일들의 목록을 리턴...
		*
		* @access public
		* @since v1
		* @param string $start_dir 시작 경로
		* @param array $arr 결과를 저장할 배열 참조
		* @param bool $prefix_start 시작경로를 파일 경로앞에 추가할지 여부
		* @param bool $add_dir_name 디렉토리 이름도 배열에 포함할지 결정
		* @param bool $recursive 하위 디렉토리 포함 여부
		* @param string pattern 검색할 파일명 패턴
		* @return array 모든 파일의 경로
		**/
		function get_filenames($start_dir, &$arr, $prefix_start = true, $add_dir_name = true, $recursive = true, $pattern = "")
		{
			$handle = opendir($start_dir) or user_error("CANNOT_OPEN_DIRECOTRY\\exit\\view|alert\{$start_dir}", E_USER_ERROR);
			while ($filename = readdir($handle))
			{
				if ($filename != "." && $filename != "..")
				{
					$path = $start_dir."/".$filename;
					if (is_dir($path))
					{
						if ($prefix_start && $add_dir_name) $arr[] = $path."/";
						else if (!$prefix_start && $add_dir_name) $arr[] = $filename."/";

						if ($recursive) $this->get_filenames($path, $arr, $prefix_start, $add_dir_name, $recursive,  $pattern);
					}
					else
					{
						if (empty($pattern) || (!empty($pattern) && ereg($pattern, $filename)))
						{
							if ($prefix_start)	$arr[] = $path;
							else				$arr[] = $filename;
						}
					}
				}
			}
			if ($handle) closedir($handle);
		}

		/**
		* $dir 에 존재하는 파일 목록을 리턴
		*
		* @access public
		* @since v2
		* @param string $dir 검색할 디렉토리
		* @param string $pattern 검사할 파일 패턴 (정규표현식)
		* @return array
		**/
		function & get_files($dir, $pattern = '')
		{
			$list = array();
			$handle = opendir($dir) or user_error("CANNOT_OPEN_DIRECOTRY : {$dir}", E_USER_ERROR);
			while ($filename = readdir($handle))
			{
				if ($filename != '.' && $filename != '..' && is_file($dir . '/' . $filename))
				{
					if (empty($pattern) || (!empty($pattern) && ereg($pattern, $filename))) $list[] = $filename;
				}
			}
			if ($handle) closedir($handle);
			return $list;
		}

		/**
		* 특정 디렉토리의 사이즈(byte)를 구한다
		*
		* @author Yangkun <yangkun7@kornet.net>
		* @since 2005-07-26
		*
		* @param string  $dir       계산할 디렉토리
		* @param boolean $recursive 디렉토리를 리커시브하게 탐색할지 여부
		* @return integer 계산된 디렉토리의 용량
		**/
		function get_size($dir, $recursive)
		{
			if ($dir[strlen($dir) - 1] != '/') $dir .= '/';
			$handle = opendir($dir) or user_error("CANNOT_OPEN_DIRECOTRY : {$dir}", E_USER_ERROR);
			$size = 0;
			while ($file = readdir($handle))
			{
				if ($file=="." or $file=="..");
				else if ($recursive && is_dir("{$dir}{$file}")) $size += $this->get_size("{$dir}{$file}", true);
				else $size += filesize("{$dir}{$file}");
			}
			return $size;
		}

		/**
		* 특정 디렉토리에서 지정한 패턴에 해당하는 파일들의 목록을 리턴...
		*
		* @version 5.1.0
		* @param string $start_dir 시작 경로
		* @param string $pattern 검사할 파일 패턴 (정규표현식)
		* @return array 모든 파일의 경로
		* @see $this::get_filenames()
		**/
		function get_sub_filenames($start_dir, $pattern='')
		{
			$list = array();

			$this->get_filenames($start_dir, &$list, true, false, true, $pattern);
			foreach ($list as $k => $v)
				$list[$k] = str_replace("{$start_dir}", "", $v);

			return $list;
		}

		/**
		* 디렉토리를 새로 생성한다.
		* @param string $dir 생성할 디렉토리 경로
		* @param int $perm 디렉토리에 설정할 퍼미션 (8진수)
		* @param boolean 디렉토리가 이미 존재하면 에러를 발생할지 여부
		* @return string 에러가 있으면 에러에 대한 내용, 없으면 공백
		**/
		function mkpath($path, $perm=0777)
		{
			$dirs = array();
			$path = preg_replace('/(\/){2,}|(\\\){1,}/', '/', $path);	//only forward-slash
			$dirs = explode("/",$path);
			$path = "";
			foreach ($dirs as $element)
			{
				$path .= $element . "/";
				$msg = $this->new_directory($path, $perm);
				if ($msg != '') return $msg;
			}

			return '';
		}

		/**
		* 파일을 새롭게 생성
		*
		* 기존에 이미 파일이 있으면 덮어 쓰거나 에러를 낸다.
		*
		* @access public
		* @since v2
		* @param string $file 생성할 파일 경로
		* @param boolean $is_overwrite 파일이 이미 존재하면 덮어쓴다. false 일때 파일이 이미 존재하면 에러
		* @return string 에러가 있으면 에러에 대한 내용, 없으면 공백
		**/
		function new_file($file, $is_overwrite = false)
		{

			if (is_file($file) && $is_overwrite) return "{$file} 은 이미 존재합니다.";

			$fp = @fopen($file, 'w');
			if (!$fp) return "{$file} 을 생성하는데 실패했습니다. 퍼미션 혹은 경로를 체크 하세요";
			@fclose($fp);

			return '';
		}

		/**
		* 중복을 제거한 새로운 파일명을 반환합니다.
		* @param string $dir 생성할 디렉토리 경로
		* @param string $to_name 파일명
		* @param string $ext 확장자
		* @version 5.3.0
		* @author hamhaja <hamhaja@nextez.co.kr>
		**/
		function new_filename($dir, $to_name, $ext)
		{
			$prefix = 1;
			$slash = ($dir[strlen($dir) - 1] != "/") ? "/" : "";
			$target = $dir . $slash . $to_name . $prefix . $ext;
			while (file_exists($target))
			{
				$prefix++;
				$target = $dir . $slash . $to_name . $prefix . $ext;
			}

			return $to_name . $prefix . $ext;
		}

		/**
		* 디렉토리를 새로 생성한다.
		*
		* @access public
		* @since v2
		* @param string $dir 생성할 디렉토리 경로
		* @param int $perm 디렉토리에 설정할 퍼미션 (8진수)
		* @param boolean 디렉토리가 이미 존재하면 에러를 발생할지 여부
		* @return string 에러가 있으면 에러에 대한 내용, 없으면 공백
		**/
		function new_directory($dir, $perm = 0777, $is_exist_error = false)
		{
			if (is_dir($dir))
			{
				if ($is_exist_error) return "{$dir} 은 이미 존재합니다.";
				else return '';
			}

			if (@mkdir($dir, $perm) == false)  return $dir . '를 생성하는데 실패했습니다. 디렉토리 권한을 확인해주세요';

			@chmod($dir, $perm);

			return '';
		}

		/**
		* 파일 업로드 처리
		* 폼에서(multipart/form-data) 넘어온 파일을 하나(!) 업로드 처리
		*
		* @access public
		* @since v1.1.0
		* @param array $files 업로드된 파일정보를 담은 배열
		* @param string $save_directory 파일을 저장할 디렉토리
		* @param string $method_on_dup 같은 이름의 파일이 존재할때 처리 방법
		*								"rename" - 업로드파일명 변경
		*								"overwrite" - 덮어쓰기
		*								"error" - 에러내기
		* @param array $exts 확장자 리스트, $allow_list 에 따라서 허가/금지 확장자 목록이 된다.
		* @param bool $allow_list 인자 $exts 가 허가 확장자 목록인가 ? FALSE 이면 업로드 불가 확장자 목록이 마지막 인자가 된다.
		* @param bool $warnning 경고 창을 띄울것인가 ?
		* @param string $to_name 파일명 변경시 사용
		* @param string $postfix 파일명 뒤에 붙일 접미사
		* @return string 저장된 파일의 경로. 에러나면 FALSE
		**/
		function upload_a_file(&$file, $save_directory, $method_on_dup = "rename",  $exts = array(), $allow_list = TRUE, $warnning = TRUE, $to_name = "", $postfix = "")
		{
			// if ($file['size'] > 0) 사이즈가 0 인 파일도 업로드 처리.
			if (empty($file['name']))
			{
				echo "
					<script language='javascript'>
						alert(\"저장할 파일이 없습니다.\");
					</script>
					";
				return FALSE;
			}
			$ext = strrchr($file['name'], ".");
			$ext = strtolower($ext);// 확장자
			$file_name = substr($file['name'], 0, strlen($file['name']) - strlen($ext));	// 확장자를 뺀 파일이름
			if (in_array($ext, $exts) != $allow_list)
			{
				if ($warnning)
				{
					$str = ($allow_list) ? "허가" : "불가";
					$ext_list = implode(" / ", $exts);
					echo "
					<script language='javascript'>
						alert(\"{$ext} 확장자를 가진 파일은 업로드 불가합니다.\\n\\n{$str} 확장자 : {$ext_list}\");
					</script>
					";
				}
				return FALSE;
			}
			// 파일명 변경
			if (!empty($to_name)) $file_name = $to_name;
			$slash = ($save_directory[strlen($save_directory) - 1] != "/") ? "/" : "";
			$target = $save_directory.$slash.$file_name.$postfix.$ext;
			$prefix = 1;
			if (file_exists($target))	// 중복 파일 존재
			{
				switch ($method_on_dup)
				{
					case "overwrite":
						//아무짓도 하지 말고 그냥 덮어쓰자.
						break;
					case "error":
						echo "
						<script language='javascript'>
							alert(\"{$target}\\n\\n같은 이름의 파일이 존재 합니다.\");
						</script>
						";
						return FALSE;
					case "rename":
						$target = $save_directory.$slash.$file_name.$prefix.$postfix.$ext;
						while (file_exists($target))
						{
							$prefix++;
							$target = $save_directory.$slash.$file_name.$prefix.$postfix.$ext;
						}
						break;
				}
			}	// endif;

			if (move_uploaded_file($file['tmp_name'], $target))
			{
				@chmod($target, 0777);
				return $target;
			}
			else
			{
				if ($warnning)
				{
					echo "
					<script language='javascript'>
						alert(\"파일 저장에 실패했습니다. 디렉토리 권한을 확인하세요\");
					</script>
					";
				}
				return false;
			}
		}

		/**
		* 특정 파일($file)에 특정내용($content)을 쓴다.
		*
		* @access public
		* @since v2
		* @param string $file 내용을 기록할 파일의 경로
		* @param string $content 기록할 내용
		* @param string $type 파일의 타입, php / htm 가능
		* @param boolean $make_backup 기존 파일이 있다면 백업할지 여부
		* @param string $new_line 개행 문자열
		* @return string 공백이면 성공, 그렇지 않으면 에러 문자열
		**/
		function write_to_file($file, $content, $type = 'php', $make_backup = true, $new_line = "\r\n")
		{
			$header        = ''; // 파일의 머리에 기록할 내용
			$tail          = ''; // 파일의 꼬리에 기록할 내용
			$comment_start = ''; // 블럭 주석의 시작
			$comment_end   = ''; // 블럭 주석의 끝
			$new_content   = ''; // 파일에 기록할 내용

			switch ($type)
			{
				case 'php':
					$header = '<?php';
					$tail = '?>';
					$comment_start = ' /*';
					$comment_end = '*/';
					break;
				case 'htm':
					$comment_start = '<!--';
					$comment_end = '-->';
					break;
				default:
					$comment_start = ';';
					break;
			}

			if ($make_backup && is_file($file)) @copy($file, $file . '.backup.' . $type);

			$new_content = $header . $comment_start . ' written by File class ' . date('Y/m/d H:i:s') . $comment_end . ' ' . $new_line;
			$new_content .= $content . $new_line;
			$new_content .= $tail;

			$fp = @fopen($file, 'w');
			if (!$fp) return $file . ' 파일을 열 수 없습니다.';
			@flock($fp, LOCK_EX);
				@fwrite($fp, $new_content);
			@flock($fp, LOCK_UN);
			@fclose($fp);

			return '';
		}


		/**
		* 특정 url를 읽어들여 data, domain, file, basename, extension, url, header, body 등의 정보를 넘겨줌.
		*
		* @access public
		* @since v2
		* @param string $url 읽어들일 특정URL
		* @return array;
		**/
		function remote_read_sock($urlstr) {
			$url = $this->parse_url2($urlstr);
			$data = "";
			$res	= @fsockopen($url[domain], 80, $strErrorNo, $strErrStr, 2);
			if($res) {
				$headerstr = "GET $urlstr HTTP/1.0\r\n";
				$headerstr.= "Host:{$url[domain]}:80\r\n";
				$headerstr.= "referer:http://$url[domain]\r\n";
				$headerstr.= "\r\n";
				fputs($res, $headerstr);
				while (!feof($res)) {
					$data.= fgets($res, 1024);
				}
				fclose($res);
				if (stristr($data, "Not Found") || stristr($data, "Bad Request") || stristr($data, "Forbidden"))
					return false;
			} else return false;

			$patten = "/Content\\-Length:\\s+([0-9]*)\\r\\n/i";
			preg_match($patten, $data, $match);
			if ($match) $filesize = $match[1];
			else $filesize = 0;

			$dataset = explode("\r\n\r\n", $data);

			return array("data"=>$data, "domain"=>$url[domain], "file"=>$url[file], "basename"=>$url[basename], "extension"=>$url[extension], "url"=>$urlstr, "header"=>$dataset[0], "body"=>$dataset[1], "filesize"=>$filesize);
		}
		function remote_read_sock2($urlstr, $gubun="\r\n\r\n") {
			$url = $this->parse_url2($urlstr);
			$data = "";
			$res	= @fsockopen($url[domain], 80, $strErrorNo, $strErrStr, 2);
			if($res) {
				$headerstr = "GET $urlstr HTTP/1.0\r\n";
				$headerstr.= "Host:{$url[domain]}:80\r\n";
				$headerstr.= "referer:http://$url[domain]\r\n";
				$headerstr.= "\r\n";
				fputs($res, $headerstr);
				while (!feof($res)) {
					$data.= fgets($res, 1024);
				}
				fclose($res);
				if (stristr($data, "Not Found") || stristr($data, "Bad Request") || stristr($data, "Forbidden"))
					return false;
			} else return false;

			$patten = "/Content\\-Length:\\s+([0-9]*)\\r\\n/i";
			preg_match($patten, $data, $match);
			if ($match) $filesize = $match[1];
			else $filesize = 0;

			$dataset = explode($gubun, $data);
			//$dataset = explode("Content-Type: text/html; charset=euc-kr", $data);

			return array("data"=>$data, "domain"=>$url[domain], "file"=>$url[file], "basename"=>$url[basename], "extension"=>$url[extension], "url"=>$urlstr, "header"=>$dataset[0], "body"=>$dataset[1], "filesize"=>$filesize);
		}

		/**
		* 특정 url를 파싱해 도메인, 파일명, 파일명 (확장자 제외), 확장자 정보를 반환해 준다.
		*
		* @access public
		* @since v2
		* @param string $url 읽어들일 특정URL
		* @return array;
		**/
		function parse_url2($urlstr) {
			$url		= parse_url($urlstr);
			$domain		= str_replace("www.", "", $url[host]);									// 도메인
			$file		= substr($url[path], strrpos($url[path], "/")+1);					// 파일명
			$basename	= str_replace("%", "", substr($file, 0, strrpos($file, ".")));	// 파일명 (확장자 제외)
			if (empty($basename)) {
				$basename = $file;
				$extension = "";
			} else	$extension	= substr($file, strrpos($file,".") + 1);						// 확장자

			return array("url"=>$urlstr, "domain"=>$domain, "file"=>$file, "basename"=>$basename, "extension"=>$extension);
		}

		/**
		* 파일사이즈 관련
		*/
		function file_cbyte($size)
		{
			if($size>=1024) return number_format($size/1024,2) . " KByte";
			elseif($size>=1024*1024) return number_format($size/(1024*1024),2) . " MByte";
			else return number_format($size,2) . " Byte";
		}

	};
}
?>